/*
 *  Copyright (c) 2018, Infineon Technologies AG
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification,are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holders nor the names of its contributors
 *  may be used to endorse or promote products derived from this software without
 *  specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  To improve the quality of the software, users are encouraged to share
 *  modifications, enhancements or bug fixes with Infineon Technologies AG
 *  dave@infineon.com).
 */
/**
 * \defgroup controller Controller
 * @{
 */
/**
 * \file	Controller.c
 * \author 	Manuel Escudero Rodriguez
 * \date	09.05.2018
 * \brief   Control functions
 */
#include <stdlib.h>
#include <DAVE.h>
#include <XMC4200.h>
#include "controller_boost.h"
#include "controller_buck.h"

/*---------------------------------------------------------------------------*/
/**
 * \brief   Updates software timers.
 * \return  None
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void timer_iterate()
{
	/*
	 * Ticks since the controller was powered. The timer will roll over after
	 * reaching the maximum, take care while programming delay routines.
	 */
    converter_ctr.time_tick ++;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   Output under voltage protection interrupt.
 * \return  None
 *
 * This will protect against short circuit at the output as well as any abnormal
 * situation leading to a sudden decrease in the output voltage.
 * This is a latching protection.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
void uvp_irq()
{
	/* All PWM signals are trapped. */
	TRAP_SET();	
	uvp_flag = 0xCC;
    converter_ctr.status |= UV_FAIL;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   Output voltage out of range protection.
 * \return  None
 *
 * This will protect against short circuit at the output as well as any abnormal
 * situation leading to the output voltage out of the normal range.
 * This will protect also against the output voltage rising above the limit.
 * This is a latching protection.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
void ovp_irq()
{
	/* All PWM signals are trapped. */
	TRAP_SET();	
	uvp_flag = 0x22;
    converter_ctr.status |= UV_FAIL;
    /*
     * The interrupt is activated by level, if the output voltage remains outside
     * of the normal range this would continue being triggered.
     * This is a latching protection. After reset the protection would be activated
     * again.
     */
    OVP_OFF();
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   Output voltage out of range protection.
 * \return  None
 *
 * This will protect against short circuit at the output as well as any abnormal
 * situation leading to the output voltage out of the normal range.
 * This will protect also against the output voltage rising above the limit.
 * This is a latching protection.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
void boost_ovp_irq()
{
	uvp_flag = 0x33;
	converter_ctr.status |= (TRAPPING | UV_FAIL);
	if(!(converter_ctr.status & BOOST_MODE))
	{
		/* All PWM signals are trapped. */
		TRAP_SET();
	}
    /*
     * The interrupt is activated by level, if the output voltage remains outside
     * of the normal range this would continue being triggered.
     * This is a latching protection. After reset the protection would be activated
     * again.
     */
    BOOST_OVP_OFF();
}
/*---------------------------------------------------------------------------*/
/**
 * \brief	Burst interrupt
 * \return 	None
 *
 * Traps the PWM signals after n switching cycles. It would be untrapped by software.
 * This way the number of pulses per burst is accurate.
 * This executes with higher priority than the control loop.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
void burst_irq()
{
	TRAP_SET();
	/* In burst mode synchronous rectifiers will be off. */
	pwm_sync_stop();
}
/*---------------------------------------------------------------------------*/
/**
 * \brief	Initialization of the voltage controller
 * \return	None
 *
 * State variables may require to be at a defined state before the voltage control
 * starts running.
 */
void voltage_ctr_init()
{
	/* Initializes output of the compensator. */
	voltage_ctr.phase_out = 0;
	/* Initializes timer counter */
	converter_ctr.time_tick = 0;
	/* 
	 * End of ramping up of reference during start-up. 
	 * To be scaled by overpower vout derating.
	 */
	voltage_ctr.reference_startup = BUCK_VOLTAGE_REF;
	/** \todo Check if this is necessary. */
	/* Protections */
    UVP_OFF();
}
/*---------------------------------------------------------------------------*/
/**
 * \brief	Initialization of the current controller
 * \return	None
 *
 * State variables may require to be at a defined state before the current control
 * starts running.
 */
void current_ctr_init()
{
    /* Iterations of the fast interrupt for next fan check */
    current_ctr.synch_il_OFF = 0;
    current_ctr.synch_il_ON = 0;
    current_ctr.hiccup_io_limit = HICCUP_SOFT_LIM;
}
/*---------------------------------------------------------------------------*/
/** Duty of the fan. [CCU_clock] */
static int32_t fan_duty = FAN_PWM_MAX;
/** Temperature control clock divider. [control_cyc/2^TEMPERATURE_CLK_DIV] */
#define TEMPERATURE_CLK_DIV	(15)	
/*---------------------------------------------------------------------------*/
/**
 * \brief   Fan status and speed control. Temperature protections
 * \return  None
 *
 * Check blocked status of the fan and sets speed as a function of temperature
 * and output current.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void temperature_ctr()
{
    static uint8_t over_temp_in_cnt; /* Filtering of over-temperature protection. */
    static uint8_t fan_dec_speed_cnt; /* Filtering of fan lowering speed. */
    static uint32_t previous_tick_fan_pwm; /* LEDs clock edge detector. [control_cyc] */
    static uint32_t previous_tick_fan_tacho; /* LEDs clock edge detector. [control_cyc] */
    static uint16_t fan_error_cnt; /* Filtering of tachometer detection error. [control_cyc] */
    int32_t fan_new_duty; /* New calculated duty of the fan. [CCU_clock] */

	converter_ctr.ntc_temperature = ADC_NTC_RESULT;
    /* Time division of the controller clock. */
	if((converter_ctr.time_tick >> TEMPERATURE_CLK_DIV) != previous_tick_fan_tacho)
	{
		previous_tick_fan_tacho = (converter_ctr.time_tick >> TEMPERATURE_CLK_DIV);
		/* Measured pulses under the minimum may trigger the fault. */
		if((FAN_CNT.ccu4_handle->slice_ptr->TIMER < FAN_MIN_TCH_PULSES) && !(converter_ctr.status & SOFT))
		{
			if(fan_error_cnt > 10)
			{
				/* Fan protection can be deactivated during debugging activities. */
				if(!parameters_RAM.debug_fan)
				{
					/* The fan seems to be blocked or running too slow */
					converter_ctr.status |= FAN_FAIL;
					TRAP_SET();
				}
			}
			else
			{
				// fan_error_cnt ++;
			}
		}
		else
		{
			if(fan_error_cnt > 0)
			{
				fan_error_cnt --;
			}
		}
		/* Clears counter value */
		FAN_CNT.ccu4_handle->slice_ptr->TCCLR = CCU4_CC4_TCCLR_TCC_Msk;
	}
    /* Time division of the controller clock */
	if((converter_ctr.time_tick >> FAN_TACHO_CLK_DIV) != previous_tick_fan_pwm)
	{
		previous_tick_fan_pwm = (converter_ctr.time_tick >> FAN_TACHO_CLK_DIV);
		/* Adjust the fan speed proportionally to the NTC measured temperature */
		int32_t ntc_fan_pwm = (FAN_PWM_MIN + (((int32_t)(NTC_55_DEG - converter_ctr.ntc_temperature) * FAN_NTC_FACTOR) >> FAN_NTC_RATIO));
		/* Adjust the fan speed proportionally to the average current measurement */
		int32_t il_fan_pwm = ((int32_t)((current_ctr.adc_io_measure - FAN_IL_OFFST) * FAN_IL_FACTOR) >> FAN_IL_RATIO);
		/* Set the fan speed to the maximum of the above requested. */
		fan_new_duty = (ntc_fan_pwm > il_fan_pwm) ? ntc_fan_pwm : il_fan_pwm;
		/* Slow down the changes in the fan duty. */
		if(fan_new_duty > fan_duty)
		{
			fan_duty ++;
			fan_dec_speed_cnt = FAN_DEC_SPEED_DIV;
		}
		else if(fan_new_duty < fan_duty)
		{
			if(fan_dec_speed_cnt > 0)
			{
				fan_dec_speed_cnt --;
			}
			else
			{
				fan_duty --;
				fan_dec_speed_cnt = FAN_DEC_SPEED_DIV;
			}
		}
		/* Maximum speed limit. */
		fan_duty = (fan_duty > FAN_PWM_MAX) ? FAN_PWM_MAX : fan_duty;
		fan_duty = (fan_duty < FAN_PWM_MIN) ? FAN_PWM_MIN : fan_duty;
		/* Updates the PWM FAN signal generation. */
		fan_duty_set(fan_duty);
	}
    /*---------------------------------------------------------------------------*/
    /* 
     * Over temperature limit checking. 
     * Note: NTC value decreases for higher temperatures. 
     */
    if(converter_ctr.ntc_temperature < NTC_TEMP_LATCH)
    {
        if (over_temp_in_cnt > 0)
        {
            over_temp_in_cnt --;
        }
        else
        {
            /* Temperature too high. Latching protection */
            converter_ctr.status |= TEMP_FAIL;
            TRAP_SET();
        }
    }
    else
    {
        over_temp_in_cnt = OVER_TEMP_IN_CNT_LIM;
    }
    /* Temperature warning limit checking. This only shows a warning, does not trap. */
    if(converter_ctr.ntc_temperature < NTC_TEMP_ALERT)
    {
    	converter_ctr.status |= TEMP_WARNING;
    }
    else
    {
    	converter_ctr.status &= ~TEMP_WARNING;
    }
    /* Die temperature measurement. */
    converter_ctr.die_temperature = die_temp_sens_value();
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   LEDs control.
 * \return  None
 *
 * Signaling of status through the onboard LEDs. Different patterns for different messages.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void leds_ctr()
{
	static uint32_t previous_tick; /* LEDs clock edge detector. */

    /* Time division of the controller clock */
    if((converter_ctr.time_tick >> LEDS_SPEED) != previous_tick)
    {
        previous_tick = (converter_ctr.time_tick >> LEDS_SPEED);
        /* Different conditions for LEDs patterns */
        if(converter_ctr.status & TEMP_FAIL)
        {
            /*
             * Temperature fault.
             * RED OFF.
             * BLUE BLINKING
             */
            RED_LED_RST();
            BLUE_LED_TGL();
        }
        else if(converter_ctr.status & START_FAIL)
        {
            /*
             * Start up failure.
             * (RED_OFF, BLUE_OFF) -> (RED_ON, BLUE_OFF) -> (RED_ON, BLUE_ON)
             */
            if(RED_LED_ON)
            {
                if(BLUE_LED_ON)
                {
                    RED_LED_RST();
                    BLUE_LED_RST();
                }
                else
                {
                    BLUE_LED_SET();
                }
            }
            else
            {
                RED_LED_SET();
            }
        }
        else if(converter_ctr.status & VIN_FAIL)
        {
            /*
             * Input voltage out of range.
             * (RED_ON, BLUE_OFF) -> (RED_OFF, BLUE_ON)
             */
            if(RED_LED_ON)
            {
                RED_LED_RST();
                BLUE_LED_SET();
            }
            else
            {
                RED_LED_SET();
                BLUE_LED_RST();
            }
        }
        else if(converter_ctr.status & FAN_FAIL)
        {
            /*
             * Not detecting FAN rotation.
             * (RED_ON, BLUE_ON) -> (RED_OFF, BLUE_OFF)
             */
            if(RED_LED_ON)
            {
                RED_LED_RST();
                BLUE_LED_RST();
            }
            else
            {
                RED_LED_SET();
                BLUE_LED_SET();
            }
        }
        else if(converter_ctr.status & UV_FAIL)
        {
            /*
             * Under or over voltage protection triggered.
             * (RED_ON, BLUE_OFF) -> (RED_OFF, BLUE_OFF)
             */
            RED_LED_TGL();
            BLUE_LED_RST();
        }
        else if(converter_ctr.status & TEMP_WARNING)
        {
        	/*
			 * Temperature warning.
			 * (RED_OFF, BLUE_OFF) -> (RED_OFF, BLUE_ON) -> (RED_ON, BLUE_ON)
			 */
			if(BLUE_LED_ON)
			{
				if(RED_LED_ON)
				{
					RED_LED_RST();
					BLUE_LED_RST();
				}
				else
				{
					RED_LED_SET();
				}
			}
			else
			{
				BLUE_LED_SET();
			}
        }
        else if(!(converter_ctr.status & OFF))
        {
            /*
             * Normal ON conditions.
             * RED_OFF, BLUE_ON
             */
            BLUE_LED_SET();
            RED_LED_RST();
        }
        else
        {
            /*
             * Normal OFF conditions
             * BLUE_OFF, RED_OFF
             */
            BLUE_LED_RST();
            RED_LED_RST();
        }
    }
}
/*---------------------------------------------------------------------------*/
/** Flash memory control clock divider. [control_cyc/2^FLASH_CLK_DIV] */
#define FLASH_CLK_DIV	(10)	
/** Flash sleep delay. 30 seconds. */
#define FLASH_SLEEP_DLY	(3000000 >> 10)
/* 30 seconds delay to set flash into sleep. */
static uint16_t flash_sleep_cnt = FLASH_SLEEP_DLY;
/*---------------------------------------------------------------------------*/
/**
 * \brief   Flash memory sleep state control.
 * \return  None
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void flash_sleep_ctr()
{
	static uint32_t time_tick;
    /*
     * Only allow storing the parameters when the converter is OFF.
     * Saving parameters into the flash takes long time.
     */
	if(converter_ctr.store_params)
	{
		if(converter_ctr.status & OFF)
		{
			if(!(XMC_FLASH_GetStatus() & XMC_FLASH_STATUS_SLEEP_MODE))
			{
				/* Sets the flag to indicate some parameters were stored already by the user. */
				parameters_RAM.master_key = MASTER_KEY;
				/* Store parameters in ROM. This last around 6.7ms */
				XMC_FLASH_EraseSector((uint32_t *)parameters_ROM);
				XMC_FLASH_ProgramPage((uint32_t *)parameters_ROM, (uint32_t *) &parameters_RAM);
				/* Reset flag after parameters have been stored. */
				converter_ctr.store_params = false;
			}
			else
			{
				/* Wakes up the flash memory fo the storage of the parameters. */
				XMC_FLASH_DisableSleepRequest();
			}
			/* Sets flash sleeping delay. */
			flash_sleep_cnt = FLASH_SLEEP_DLY;
		}
		else
		{
			/* Reset flag without storing if the converter is not off. */
			converter_ctr.store_params = false;
		}
	}
	/* Flash timer tick division. */
	if((converter_ctr.time_tick >> FLASH_CLK_DIV) != time_tick)
	{
		time_tick = (converter_ctr.time_tick >> FLASH_CLK_DIV);
		if(flash_sleep_cnt > 0)
		{
			flash_sleep_cnt --;
		}
		else
		{
//			/* Flash into sleep mode after delay. */
//			if(!(XMC_FLASH_GetStatus() & XMC_FLASH_STATUS_SLEEP_MODE))
//			{
//				XMC_FLASH_EnableSleepRequest();
//			}			
		}
	}
}
/*---------------------------------------------------------------------------*/
/** Flash memory control clock divider. [control_cyc] */
#define DEBOUNCING_CNT	(1000)
/*---------------------------------------------------------------------------*/
/**
 * \brief   Buck boost mode selection.
 * \return  None
 * External switch selects mode of converter. 
 * First iteration, value of switch is directly read.
 * After first iteration, debouncing filter is applied to state of switch.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void switch_mode_ctr()
{
	static uint16_t mode_cnt; /* Filter of switch signal (debouncing). [control_cyc] */
	static uint8_t first_check; /* First iteration no debouncing (start up). [Boolean] */
	
	/* After first iteration, debouncing filter active. */
	if(first_check == 1)
	{
		/* Status is BOOST mode. */
		if(converter_ctr.status & BOOST_MODE)
		{
			/* Checks mode from MODE pin. */
			if(!IO_BOOST_MODE)
			{
				if(mode_cnt > DEBOUNCING_CNT)
				{
					/* Turns OFF converter during mode exchange. */
					converter_set_off();
					buck_mode_set = true;
				}
				else
				{
					mode_cnt ++;
				}
			} 
			else
			{
				mode_cnt = 0;
			}
		}
		/* Status is BUCK mode. */
		else
		{
			/* Checks mode from MODE pin. */
			if(IO_BOOST_MODE)
			{
				if(mode_cnt > DEBOUNCING_CNT)
				{		
					boost_mode_init();
				}
				else
				{
					mode_cnt ++;
				}
			}
			else
			{
				mode_cnt = 0;
			}
		}
	}
	/* First iteration, no debouncing, read state of switch. */
	else
	{
		/* Status is BOOST mode. */
		if(converter_ctr.status & BOOST_MODE)
		{
			/* Checks mode from MODE pin. */
			if(!IO_BOOST_MODE)
			{
				/* Turns OFF converter during mode exchange. */
				converter_set_off();
				buck_mode_set = 1;
			} 
		}
		/* Status is BUCK mode. */
		else
		{
			/* Checks mode from MODE pin. */
			if(IO_BOOST_MODE)
			{	
				boost_mode_init();
			}
		}
		first_check = 1;
	}
	if(buck_mode_set == 1)
	{
		if(!(converter_ctr.status & TRAPPING))
		{
			if(converter_ctr.status & OFF)
			{
				buck_mode_init();
				buck_mode_set = 0;
			}
			else
			{
				/* Turns OFF converter during mode exchange. */
				converter_set_off();
			}
		}
	}
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   Low priority tasks
 * \return  None
 *
 * Infinity loop containing the low priority tasks executed during the remaining
 * CPU time out of the control loop.
 * Also known as background tasks.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
void low_priority_task()
{
    while(1)
    { 
		ADC_IL_RESULT_test = ADC_IL_RESULT;
		ADC_IO_RESULT_test = ADC_IO_RESULT;

		if(ADC_IL_RESULT_max_test < ADC_IL_RESULT)
			ADC_IL_RESULT_max_test = ADC_IL_RESULT;
		if(current_ctr.adc_io_zero >= ADC_IO_RESULT)
		{
			ADC_IO_RESULT_max_test = current_ctr.adc_io_zero - ADC_IO_RESULT;
			ADC_IO_RESULT_PN_test = 0xA0;
		}
		else
		{
			ADC_IO_RESULT_max_test = ADC_IO_RESULT - current_ctr.adc_io_zero;
			ADC_IO_RESULT_PN_test = 0xA1;
		}

        switch_mode_ctr();
		if(converter_ctr.status & BOOST_MODE)
		{
			boost_start_up_iterate();
			boost_brown_in_off_ctr();
			boost_ovp_uvp_ctr_iterate();
		}
		else
		{
			buck_start_up_iterate();
			buck_brown_in_off_ctr();
		}
        uart_transmit();
        uart_receive();
        temperature_ctr();
        if(converter_ctr.status & BOOST_MODE)
        {
            boost_current_ratio_ctr();
            boost_power_limit_ctr();
        }
        else
        {
            buck_current_ratio_ctr();
            buck_power_limit_ctr();
        }
        leds_ctr();
        flash_sleep_ctr();
	// if(time_count_while<0xFFFFFFFF)
	// 	time_count_while++;
	// else
	// 	time_count_while = 0;
    }
}
/*---------------------------------------------------------------------------*/
/**
 * \brief	High frequency interrupt
 * \return 	None
 *
 * This interrupt is triggered each two switching cycles (@50KHz).
 * The trigger signal is generated by the current measurement.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
void high_freq_irq()
{
	// if(time_count_power<0xFFFFFFFF)
	// 	time_count_power++;
	// else
	// 	time_count_power = 0;
	//RED_LED_SET();
	if(converter_ctr.status & BOOST_MODE)
	{
		boost_voltage_ctr_iterate();
		boost_current_ctr_iterate();
	}
	else
	{
		buck_voltage_ctr_iterate();
		buck_current_ctr_iterate();
	}
	timer_iterate();
	//RED_LED_RST();
}
/** }@*/
